import traceback
from typing import Any

from vnpy.trader.constant import Interval, Direction

from vnpy_ctastrategy.backtesting import BacktestingEngine

from vnpy_ctastrategy import (
    CtaTemplate,
    StopOrder,
    TickData,
    BarData,
    TradeData,
    OrderData,
    BarGenerator,
    ArrayManager,
)


class ShortOnlyStrategy(CtaTemplate):
    """"""

    author = "Cjie Wong"

    fixed_size = 1
    ma_fast_window = 21
    ma_slow_window = 60
    # fixed stop loss
    fixed_sl = 5

    fast_ma0 = 0.0
    fast_ma1 = 0.0
    slow_ma0 = 0.0
    slow_ma1 = 0.0
    macd0 = 0.0
    macd1 = 0.0
    signal0 = 0.0
    signal1 = 0.0
    cross_below = False
    # 在策略类中定义一个变量来存储前波峰的高度
    previous_peak = 0
    # 定义一个变量来存储上一根bar的最高价
    last_high = 0
    # 定义一个变量来存储上上根bar的最高价
    before_last_high = 0
    # 做空进入的价格
    short_entry = 0
    # 移动止损价格
    stop_loss_price = 0
    # 移动止盈价格
    take_profit_price = 0

    parameters = ["fixed_size", "ma_fast_window", "ma_slow_window", "fixed_sl"]

    variables = ["fast_ma0", "fast_ma1", "slow_ma0", "slow_ma1", "cross_below", "macd0", "macd1", "signal0", "signal1",
                 "previous_peak",
                 "last_high", "before_last_high", "short_entry", "stop_loss_price", "take_profit_price"]

    def __init__(self, cta_engine: Any, strategy_name: str, vt_symbol: str, setting: dict) -> None:
        super().__init__(cta_engine, strategy_name, vt_symbol, setting)
        self.bg15min = BarGenerator(self.on_bar, 15, self.on_15min_bar)
        self.bg2h = BarGenerator(self.on_bar, 2, self.on_2hour_bar, Interval.HOUR)
        self.am15min = ArrayManager()
        self.am2hour = ArrayManager()
        self.is_back_test = isinstance(cta_engine, BacktestingEngine)

    def on_init(self) -> None:
        self.write_log("策略初始化")
        self.load_bar(40)

    def on_start(self) -> None:
        self.write_log("策略启动")

    def on_stop(self) -> None:
        self.write_log("策略停止")

    def on_tick(self, tick: TickData) -> None:
        self.bg15min.update_tick(tick)

    def on_bar(self, bar: BarData) -> None:

        if not self.is_back_test:
            if bar.gateway_name == 'CTP':
                print(f"【ShortOnlyStrategy】bar={bar}")
        self.cancel_all()

        self.bg2h.update_bar(bar)
        self.bg15min.update_bar(bar)

        # 如果上一根bar的最高价大于上上根bar的最高价和当前bar的最高价，
        # 则上一根bar是一个波峰，更新前波峰的高度
        if self.last_high > self.before_last_high and self.last_high > bar.high_price:
            self.previous_peak = self.last_high

        # 更新上上根bar的最高价和上一根bar的最高价
        self.before_last_high = self.last_high
        self.last_high = bar.high_price

        # 检查当前价格是否触及前波峰加固定点的价格
        self.stop_loss_price = self.previous_peak + self.fixed_sl
        # 1:1固定止盈价格
        if self.short_entry > 0:
            self.take_profit_price = self.short_entry - abs(self.stop_loss_price - self.short_entry)
        if self.pos < 0 < self.take_profit_price:
            self.cover(self.take_profit_price, abs(self.pos))

        # 如果当前价格大于等于止损价格，执行止损操作
        if self.pos < 0 and bar.close_price >= self.stop_loss_price:
            # 在这里执行止损操作，例如，平仓
            msg = f"触发止损，执行买入平仓.bar={bar}"
            print(msg)
            self.write_log(msg)
            self.cover(bar.close_price + 5, abs(self.pos))

    def on_15min_bar(self, bar: BarData):
        """
        进场周期15min，MACD快线下穿0轴。开空仓。
        """
        self.am15min.update_bar(bar)
        if not self.am15min.inited:
            print(f"am15min not inited, skip , count={self.am15min.count},size={self.am15min.size}")
            return
        macd, signal, hist = self.am15min.macd(12, 26, 9, True)
        # MACD快线下穿0轴指的是macd的值由正数变为负数
        self.macd0 = macd[-1]
        self.macd1 = macd[-2]
        self.signal0 = signal[-1]
        self.signal1 = signal[-2]
        if self.cross_below:
            if self.macd1 > 0 > self.macd0 and self.pos == 0:
                # 以当前k线的收盘价-5块钱的价格 卖空{fixed_size}数目的标的物
                self.short(bar.close_price - 5, self.fixed_size)
        # 0轴下方金叉止盈
        # if self.pos < 0 and self.macd0 < 0 and self.signal1 > self.macd1 and self.signal0 < self.macd0:
        #     msg = f"macd 0轴下方金叉，触发止盈.bar={bar}"
        #     print(msg)
        #     self.write_log(msg)
        #     self.cover(bar.close_price + 5, abs(self.pos))

    def on_2hour_bar(self, bar: BarData):
        """
        大周期2h，EMA21&60死叉，准备
        """
        self.am2hour.update_bar(bar)
        if not self.am2hour.inited:
            print(f"am2hour not inited, skip , count={self.am2hour.count},size={self.am2hour.size}")
            return
        print(f"load 2hour bar={bar}")
        fast_ma = self.am2hour.sma(self.ma_fast_window, array=True)
        self.fast_ma0 = fast_ma[-1]
        self.fast_ma1 = fast_ma[-2]
        slow_ma = self.am2hour.sma(self.ma_slow_window, array=True)
        self.slow_ma0 = slow_ma[-1]
        self.slow_ma1 = slow_ma[-2]
        # print(f"fast_ma0={self.fast_ma0},fast_ma1={self.fast_ma1},self.slow_ma0={self.slow_ma0},self.slow_ma1={self.slow_ma1}")
        # 判断死叉
        if self.fast_ma0 <= self.slow_ma0 and self.fast_ma1 > self.slow_ma1:
            self.cross_below = True
            print(f"双均线出现死叉,准备交易，bar={bar}")
        # 判断金叉
        if self.fast_ma0 >= self.slow_ma0 and self.fast_ma1 < self.slow_ma1:
            print(f"双均线出现金叉,停止交易，bar={bar}")
            self.cross_below = False

    def on_trade(self, trade: TradeData) -> None:
        if self.pos != 0:
            # 买入平仓
            if trade.direction == Direction.LONG:
                print(
                    f"买入平仓 交易价格={trade.price},入场价格={self.short_entry},价差盈利={self.short_entry - trade.price},volume={trade.volume}")
            else:
                print(f"卖出开仓 交易价格={trade.price},volume={trade.volume}")
                self.short_entry = trade.price
        try:
            self.push_msg(f"交易一笔成功，direction={trade.direction}，offset{trade.offset},price={trade.price}")
        except AttributeError:
            pass
        except Exception:
            msg: str = f"触发异常已停止\n{traceback.format_exc()}"
            self.write_log(msg)
            print(f"推送消息失败:{msg}")

        self.put_event()
